home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1980 Regents of the University of California.
- * All rights reserved. The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
- */
-
- #ifndef lint
- static char *sccsid = "@(#)ex_vops.c 7.8 (Berkeley) 3/9/87; 1.2 (Bellcore) 87/04/24";
- #endif not lint
-
- #include "ex.h"
- #include "ex_tty.h"
- #include "ex_vis.h"
-
- /*
- * This file defines the operation sequences which interface the
- * logical changes to the file buffer with the internal and external
- * display representations.
- */
-
- /*
- * Undo.
- *
- * Undo is accomplished in two ways. We often for small changes in the
- * current line know how (in terms of a change operator) how the change
- * occurred. Thus on an intelligent terminal we can undo the operation
- * by another such operation, using insert and delete character
- * stuff. The pointers vU[AD][12] index the buffer vutmp when this
- * is possible and provide the necessary information.
- *
- * The other case is that the change involved multiple lines or that
- * we have moved away from the line or forgotten how the change was
- * accomplished. In this case we do a redisplay and hope that the
- * low level optimization routines (which don't look for winning
- * via insert/delete character) will not lose too badly.
- */
- char *vUA1, *vUA2;
- char *vUD1, *vUD2;
-
- ex_vUndo()
- {
-
- /*
- * Avoid UU which clobbers ability to do u.
- */
- if (vundkind == VCAPU || vUNDdot != dot) {
- beep();
- return;
- }
- CP(vutmp, linebuf);
- vUD1 = linebuf; vUD2 = strend(linebuf);
- putmk1(dot, vUNDsav);
- getDOT();
- vUA1 = linebuf; vUA2 = strend(linebuf);
- vundkind = VCAPU;
- if (state == ONEOPEN || state == HARDOPEN) {
- vjumpto(dot, vUNDcurs, 0);
- return;
- }
- vdirty(vcline, 1);
- vsyncCL();
- cursor = linebuf;
- vfixcurs();
- }
-
- vundo(show)
- bool show; /* if true update the screen */
- {
- register int cnt;
- register line *addr;
- register char *cp;
- char temp[LBSIZE];
- bool savenote;
- int (*OO)();
- short oldhold = hold;
-
- switch (vundkind) {
-
- case VMANYINS:
- wcursor = 0;
- addr1 = undap1;
- addr2 = undap2 - 1;
- vsave();
- YANKreg('1');
- notecnt = 0;
- /* fall into ... */
-
- case VMANY:
- case VMCHNG:
- vsave();
- addr = dot - vcline;
- notecnt = 1;
- if (undkind == UNDPUT && undap1 == undap2) {
- beep();
- break;
- }
- /*
- * Undo() call below basically replaces undap1 to undap2-1
- * with dol through unddol-1. Hack screen image to
- * reflect this replacement.
- */
- if (show)
- if (undkind == UNDMOVE)
- vdirty(0, LINES);
- else
- vreplace(undap1 - addr, undap2 - undap1,
- undkind == UNDPUT ? 0 : unddol - dol);
- savenote = notecnt;
- undo(1);
- if (show && (vundkind != VMCHNG || addr != dot))
- killU();
- vundkind = VMANY;
- cnt = dot - addr;
- if (cnt < 0 || cnt > vcnt || state != VISUAL) {
- if (show)
- vjumpto(dot, NOSTR, '.');
- break;
- }
- if (!savenote)
- notecnt = 0;
- if (show) {
- vcline = cnt;
- vrepaint(vmcurs);
- }
- vmcurs = 0;
- break;
-
- case VCHNG:
- case VCAPU:
- vundkind = VCHNG;
- strcpy(temp, vutmp);
- strcpy(vutmp, linebuf);
- doomed = column(vUA2 - 1) - column(vUA1 - 1);
- strcLIN(temp);
- cp = vUA1; vUA1 = vUD1; vUD1 = cp;
- cp = vUA2; vUA2 = vUD2; vUD2 = cp;
- if (!show)
- break;
- cursor = vUD1;
- if (state == HARDOPEN) {
- doomed = 0;
- vsave();
- vopen(dot, WBOT);
- vnline(cursor);
- break;
- }
- /*
- * Pseudo insert command.
- */
- vcursat(cursor);
- OO = Outchar; Outchar = vinschar; hold |= HOLDQIK;
- vprepins();
- temp[vUA2 - linebuf] = 0;
- for (cp = &temp[vUA1 - linebuf]; *cp;)
- ex_putchar(*cp++);
- Outchar = OO; hold = oldhold;
- endim();
- physdc(cindent(), cindent() + doomed);
- doomed = 0;
- vdirty(vcline, 1);
- vsyncCL();
- if (cursor > linebuf && cursor >= strend(linebuf))
- cursor--;
- vfixcurs();
- break;
-
- case VNONE:
- beep();
- break;
- }
- }
-
- /*
- * Routine to handle a change inside a macro.
- * Fromvis is true if we were called from a visual command (as
- * opposed to an ex command). This has nothing to do with being
- * in open/visual mode as :s/foo/bar is not fromvis.
- */
- vmacchng(fromvis)
- bool fromvis;
- {
- line *savedot, *savedol;
- char *savecursor;
- char savelb[LBSIZE];
- int nlines, more;
- int copyw(), copywR();
-
- if (!inopen)
- return;
- if (!vmacp)
- vch_mac = VC_NOTINMAC;
- #ifdef TRACE
- if (trace)
- fprintf(trace, "vmacchng, vch_mac=%d, linebuf='%s', *dot=%o\n", vch_mac, linebuf, *dot);
- #endif
- if (vmacp && fromvis)
- vsave();
- #ifdef TRACE
- if (trace)
- fprintf(trace, "after vsave, linebuf='%s', *dot=%o\n", linebuf, *dot);
- #endif
- switch(vch_mac) {
- case VC_NOCHANGE:
- vch_mac = VC_ONECHANGE;
- break;
- case VC_ONECHANGE:
- /* Save current state somewhere */
- #ifdef TRACE
- vudump("before vmacchng hairy case");
- #endif
- savedot = dot; savedol = dol; savecursor = cursor;
- CP(savelb, linebuf);
- nlines = dol - zero;
- while ((line *) endcore - truedol < nlines)
- if (morelines() < 0) {
- dot = savedot;
- dol = savedol;
- cursor = savecursor;
- CP(linebuf, savelb);
- error("Out of memory@- too many lines to undo");
- }
- copyw(truedol+1, zero+1, nlines);
- truedol += nlines;
-
- #ifdef TRACE
- visdump("before vundo");
- #endif
- /* Restore state as it was at beginning of macro */
- vundo(0);
- #ifdef TRACE
- visdump("after vundo");
- vudump("after vundo");
- #endif
-
- /* Do the saveall we should have done then */
- saveall();
- #ifdef TRACE
- vudump("after saveall");
- #endif
-
- /* Restore current state from where saved */
- more = savedol - dol; /* amount we shift everything by */
- if (more)
- (*(more>0 ? copywR : copyw))(savedol+1, dol+1, truedol-dol);
- unddol += more; truedol += more; undap2 += more;
-
- truedol -= nlines;
- copyw(zero+1, truedol+1, nlines);
- dot = savedot; dol = savedol ; cursor = savecursor;
- CP(linebuf, savelb);
- vch_mac = VC_MANYCHANGE;
-
- /* Arrange that no further undo saving happens within macro */
- otchng = tchng; /* Copied this line blindly - bug? */
- inopen = -1; /* no need to save since it had to be 1 or -1 before */
- vundkind = VMANY;
- #ifdef TRACE
- vudump("after vmacchng");
- #endif
- break;
- case VC_NOTINMAC:
- case VC_MANYCHANGE:
- /* Nothing to do for various reasons. */
- break;
- }
- }
-
- /*
- * Initialize undo information before an append.
- */
- vnoapp()
- {
-
- vUD1 = vUD2 = cursor;
- }
-
- /*
- * All the rest of the motion sequences have one or more
- * cases to deal with. In the case wdot == 0, operation
- * is totally within current line, from cursor to wcursor.
- * If wdot is given, but wcursor is 0, then operation affects
- * the inclusive line range. The hardest case is when both wdot
- * and wcursor are given, then operation affects from line dot at
- * cursor to line wdot at wcursor.
- */
-
- /*
- * Move is simple, except for moving onto new lines in hardcopy open mode.
- */
- vmove()
- {
- register int cnt;
-
- if (wdot) {
- if (wdot < one || wdot > dol) {
- beep();
- return;
- }
- cnt = wdot - dot;
- wdot = NOLINE;
- if (cnt)
- killU();
- vupdown(cnt, wcursor);
- return;
- }
-
- /*
- * When we move onto a new line, save information for U undo.
- */
- if (vUNDdot != dot) {
- vUNDsav = *dot;
- vUNDcurs = wcursor;
- vUNDdot = dot;
- }
-
- /*
- * In hardcopy open, type characters to left of cursor
- * on new line, or back cursor up if its to left of where we are.
- * In any case if the current line is ``rubbled'' i.e. has trashy
- * looking overstrikes on it or \'s from deletes, we reprint
- * so it is more comprehensible (and also because we can't work
- * if we let it get more out of sync since column() won't work right.
- */
- if (state == HARDOPEN) {
- register char *cp;
- if (rubble) {
- register int c;
- int oldhold = hold;
-
- sethard();
- cp = wcursor;
- c = *cp;
- *cp = 0;
- hold |= HOLDDOL;
- ignore(vreopen(WTOP, lineDOT(), vcline));
- hold = oldhold;
- *cp = c;
- } else if (wcursor > cursor) {
- vfixcurs();
- for (cp = cursor; *cp && cp < wcursor;) {
- register int c = *cp++ & TRIM;
-
- ex_putchar(c ? c : ' ');
- }
- }
- }
- vsetcurs(wcursor);
- }
-
- /*
- * Delete operator.
- *
- * Hard case of deleting a range where both wcursor and wdot
- * are specified is treated as a special case of change and handled
- * by vchange (although vchange may pass it back if it degenerates
- * to a full line range delete.)
- */
- vdelete(c)
- char c;
- {
- register char *cp;
- register int i;
-
- if (wdot) {
- if (wcursor) {
- vchange('d');
- return;
- }
- if ((i = xdw()) < 0)
- return;
- if (state != VISUAL) {
- vgoto(LINE(0), 0);
- vputchar('@');
- }
- wdot = dot;
- vremote(i, ex_delete, 0);
- notenam = "delete";
- DEL[0] = 0;
- killU();
- vreplace(vcline, i, 0);
- if (wdot > dol)
- vcline--;
- vrepaint(NOSTR);
- return;
- }
- if (wcursor < linebuf)
- wcursor = linebuf;
- if (cursor == wcursor) {
- beep();
- return;
- }
- i = vdcMID();
- cp = cursor;
- setDEL();
- CP(cp, wcursor);
- if (cp > linebuf && (cp[0] == 0 || c == '#'))
- cp--;
- if (state == HARDOPEN) {
- bleep(i, cp);
- cursor = cp;
- return;
- }
- physdc(column(cursor - 1), i);
- DEPTH(vcline) = 0;
- ignore(vreopen(LINE(vcline), lineDOT(), vcline));
- vsyncCL();
- vsetcurs(cp);
- }
-
- /*
- * Change operator.
- *
- * In a single line we mark the end of the changed area with '$'.
- * On multiple whole lines, we clear the lines first.
- * Across lines with both wcursor and wdot given, we delete
- * and sync then append (but one operation for undo).
- */
- vchange(c)
- char c;
- {
- register char *cp;
- register int i, ind, cnt;
- line *addr;
-
- if (wdot) {
- /*
- * Change/delete of lines or across line boundaries.
- */
- if ((cnt = xdw()) < 0)
- return;
- getDOT();
- if (wcursor && cnt == 1) {
- /*
- * Not really.
- */
- wdot = 0;
- if (c == 'd') {
- vdelete(c);
- return;
- }
- goto smallchange;
- }
- if (cursor && wcursor) {
- /*
- * Across line boundaries, but not
- * necessarily whole lines.
- * Construct what will be left.
- */
- *cursor = 0;
- strcpy(genbuf, linebuf);
- getline(*wdot);
- if (strlen(genbuf) + strlen(wcursor) > LBSIZE - 2) {
- getDOT();
- beep();
- return;
- }
- strcat(genbuf, wcursor);
- if (c == 'd' && *vpastwh(genbuf) == 0) {
- /*
- * Although this is a delete
- * spanning line boundaries, what
- * would be left is all white space,
- * so take it all away.
- */
- wcursor = 0;
- getDOT();
- op = 0;
- notpart(lastreg);
- notpart('1');
- vdelete(c);
- return;
- }
- ind = -1;
- } else if (c == 'd' && wcursor == 0) {
- vdelete(c);
- return;
- } else
- #ifdef LISPCODE
- /*
- * We are just substituting text for whole lines,
- * so determine the first autoindent.
- */
- if (value(LISP) && value(AUTOINDENT))
- ind = lindent(dot);
- else
- #endif
- ind = whitecnt(linebuf);
- i = vcline >= 0 ? LINE(vcline) : WTOP;
-
- /*
- * Delete the lines from the buffer,
- * and remember how the partial stuff came about in
- * case we are told to put.
- */
- addr = dot;
- vremote(cnt, ex_delete, 0);
- setpk();
- notenam = "delete";
- if (c != 'd')
- notenam = "change";
- /*
- * If DEL[0] were nonzero, put would put it back
- * rather than the deleted lines.
- */
- DEL[0] = 0;
- if (cnt > 1)
- killU();
-
- /*
- * Now hack the screen image coordination.
- */
- vreplace(vcline, cnt, 0);
- wdot = NOLINE;
- ignore(noteit(0));
- vcline--;
- if (addr <= dol)
- dot--;
-
- /*
- * If this is a across line delete/change,
- * cursor stays where it is; just splice together the pieces
- * of the new line. Otherwise generate a autoindent
- * after a S command.
- */
- if (ind >= 0) {
- *genindent(ind) = 0;
- vdoappend(genbuf);
- } else {
- vmcurs = cursor;
- strcLIN(genbuf);
- vdoappend(linebuf);
- }
-
- /*
- * Indicate a change on hardcopies by
- * erasing the current line.
- */
- if (c != 'd' && state != VISUAL && state != HARDOPEN) {
- int oldhold = hold;
-
- hold |= HOLDAT, vclrlin(i, dot), hold = oldhold;
- }
-
- /*
- * Open the line (logically) on the screen, and
- * update the screen tail. Unless we are really a delete
- * go off and gather up inserted characters.
- */
- vcline++;
- if (vcline < 0)
- vcline = 0;
- vopen(dot, i);
- vsyncCL();
- ignore(noteit(1));
- if (c != 'd') {
- if (ind >= 0) {
- cursor = linebuf;
- linebuf[0] = 0;
- vfixcurs();
- } else {
- ind = 0;
- vcursat(cursor);
- }
- vappend('x', 1, ind);
- return;
- }
- if (*cursor == 0 && cursor > linebuf)
- cursor--;
- vrepaint(cursor);
- return;
- }
-
- smallchange:
- /*
- * The rest of this is just low level hacking on changes
- * of small numbers of characters.
- */
- if (wcursor < linebuf)
- wcursor = linebuf;
- if (cursor == wcursor) {
- beep();
- return;
- }
- i = vdcMID();
- cp = cursor;
- if (state != HARDOPEN)
- vfixcurs();
-
- /*
- * Put out the \\'s indicating changed text in hardcopy,
- * or mark the end of the change with $ if not hardcopy.
- */
- if (state == HARDOPEN)
- bleep(i, cp);
- else {
- vcursbef(wcursor);
- ex_putchar('$');
- i = cindent();
- }
-
- /*
- * Remember the deleted text for possible put,
- * and then prepare and execute the input portion of the change.
- */
- cursor = cp;
- setDEL();
- CP(cursor, wcursor);
- if (state != HARDOPEN) {
- vcursaft(cursor - 1);
- doomed = i - cindent();
- } else {
- /*
- sethard();
- wcursor = cursor;
- cursor = linebuf;
- vgoto(outline, value(NUMBER) << 3);
- vmove();
- */
- doomed = 0;
- }
- prepapp();
- vappend('c', 1, 0);
- }
-
- /*
- * Open new lines.
- *
- * Tricky thing here is slowopen. This causes display updating
- * to be held off so that 300 baud dumb terminals don't lose badly.
- * This also suppressed counts, which otherwise say how many blank
- * space to open up. Counts are also suppressed on intelligent terminals.
- * Actually counts are obsoleted, since if your terminal is slow
- * you are better off with slowopen.
- */
- voOpen(c, cnt)
- int c; /* mjm: char --> int */
- register int cnt;
- {
- register int ind = 0, i;
- short oldhold = hold;
- #ifdef SIGWINCH
- int oldmask;
- #endif
-
- if (value(SLOWOPEN) || value(REDRAW) && AL && DL)
- cnt = 1;
- #ifdef SIGWINCH
- oldmask = sigblock(sigmask(SIGWINCH));
- #endif
- vsave();
- setLAST();
- if (value(AUTOINDENT))
- ind = whitecnt(linebuf);
- if (c == 'O') {
- vcline--;
- dot--;
- if (dot > zero)
- getDOT();
- }
- if (value(AUTOINDENT)) {
- #ifdef LISPCODE
- if (value(LISP))
- ind = lindent(dot + 1);
- #endif
- }
- killU();
- prepapp();
- if (FIXUNDO)
- vundkind = VMANY;
- if (state != VISUAL)
- c = WBOT + 1;
- else {
- c = vcline < 0 ? WTOP - cnt : LINE(vcline) + DEPTH(vcline);
- if (c < ex_ZERO)
- c = ex_ZERO;
- i = LINE(vcline + 1) - c;
- if (i < cnt && c <= WBOT && (!AL || !DL))
- vinslin(c, cnt - i, vcline);
- }
- *genindent(ind) = 0;
- vdoappend(genbuf);
- vcline++;
- oldhold = hold;
- hold |= HOLDROL;
- vopen(dot, c);
- hold = oldhold;
- if (value(SLOWOPEN))
- /*
- * Oh, so lazy!
- */
- vscrap();
- else
- vsync1(LINE(vcline));
- cursor = linebuf;
- linebuf[0] = 0;
- vappend('o', 1, ind);
- #ifdef SIGWINCH
- (void)sigsetmask(oldmask);
- #endif
- }
-
- /*
- * > < and = shift operators.
- *
- * Note that =, which aligns lisp, is just a ragged sort of shift,
- * since it never distributes text between lines.
- */
- char vshnam[2] = { 'x', 0 };
-
- vshftop()
- {
- register line *addr;
- register int cnt;
-
- if ((cnt = xdw()) < 0)
- return;
- addr = dot;
- vremote(cnt, vshift, 0);
- vshnam[0] = op;
- notenam = vshnam;
- dot = addr;
- vreplace(vcline, cnt, cnt);
- if (state == HARDOPEN)
- vcnt = 0;
- vrepaint(NOSTR);
- }
-
- /*
- * !.
- *
- * Filter portions of the buffer through unix commands.
- */
- vfilter()
- {
- register line *addr;
- register int cnt;
- char *oglobp;
- short d;
-
- if ((cnt = xdw()) < 0)
- return;
- if (vglobp)
- vglobp = uxb;
- if (readecho('!'))
- return;
- oglobp = globp; globp = genbuf + 1;
- d = peekc; ungetchar(0);
- CATCH
- fixech();
- unix0(0);
- ONERR
- splitw = 0;
- ungetchar(d);
- vrepaint(cursor);
- globp = oglobp;
- return;
- ENDCATCH
- ungetchar(d); globp = oglobp;
- addr = dot;
- CATCH
- vgoto(WECHO, 0); flusho();
- vremote(cnt, filter, 2);
- ONERR
- vdirty(0, LINES);
- ENDCATCH
- if (dot == zero && dol > zero)
- dot = one;
- splitw = 0;
- notenam = "";
- /*
- * BUG: we shouldn't be depending on what undap2 and undap1 are,
- * since we may be inside a macro. What's really wanted is the
- * number of lines we read from the filter. However, the mistake
- * will be an overestimate so it only results in extra work,
- * it shouldn't cause any real screwups.
- */
- vreplace(vcline, cnt, undap2 - undap1);
- dot = addr;
- if (dot > dol) {
- dot--;
- vcline--;
- }
- vrepaint(NOSTR);
- }
-
- /*
- * Xdw exchanges dot and wdot if appropriate and also checks
- * that wdot is reasonable. Its name comes from
- * xchange dotand wdot
- */
- xdw()
- {
- register char *cp;
- register int cnt;
- /*
- register int notp = 0;
- */
-
- if (wdot == NOLINE || wdot < one || wdot > dol) {
- beep();
- return (-1);
- }
- vsave();
- setLAST();
- if (dot > wdot || (dot == wdot && wcursor != 0 && cursor > wcursor)) {
- register line *addr;
-
- vcline -= dot - wdot;
- addr = dot; dot = wdot; wdot = addr;
- cp = cursor; cursor = wcursor; wcursor = cp;
- }
- /*
- * If a region is specified but wcursor is at the begining
- * of the last line, then we move it to be the end of the
- * previous line (actually off the end).
- */
- if (cursor && wcursor == linebuf && wdot > dot) {
- wdot--;
- getDOT();
- if (vpastwh(linebuf) >= cursor)
- wcursor = 0;
- else {
- getline(*wdot);
- wcursor = strend(linebuf);
- getDOT();
- }
- /*
- * Should prepare in caller for possible dot == wdot.
- */
- }
- cnt = wdot - dot + 1;
- if (vreg) {
- vremote(cnt, YANKreg, vreg);
- /*
- if (notp)
- notpart(vreg);
- */
- }
-
- /*
- * Kill buffer code. If delete operator is c or d, then save
- * the region in numbered buffers.
- *
- * BUG: This may be somewhat inefficient due
- * to the way named buffer are implemented,
- * necessitating some optimization.
- */
- vreg = 0;
- if (any(op, "cd")) {
- vremote(cnt, YANKreg, '1');
- /*
- if (notp)
- notpart('1');
- */
- }
- return (cnt);
- }
-
- /*
- * Routine for vremote to call to implement shifts.
- */
- vshift()
- {
-
- shift(op, 1);
- }
-
- /*
- * Replace a single character with the next input character.
- * A funny kind of insert.
- */
- vrep(cnt)
- register int cnt;
- {
- register int i, c;
-
- if (cnt > strlen(cursor)) {
- beep();
- return;
- }
- i = column(cursor + cnt - 1);
- vcursat(cursor);
- doomed = i - cindent();
- if (!vglobp) {
- c = getesc();
- if (c == 0) {
- vfixcurs();
- return;
- }
- ungetkey(c);
- }
- CP(vutmp, linebuf);
- if (FIXUNDO)
- vundkind = VCHNG;
- wcursor = cursor + cnt;
- vUD1 = cursor; vUD2 = wcursor;
- CP(cursor, wcursor);
- prepapp();
- vappend('r', cnt, 0);
- *lastcp++ = INS[0];
- setLAST();
- }
-
- /*
- * Yank.
- *
- * Yanking to string registers occurs for free (essentially)
- * in the routine xdw().
- */
- vyankit()
- {
- register int cnt;
-
- if (wdot) {
- if ((cnt = xdw()) < 0)
- return;
- vremote(cnt, yank, 0);
- setpk();
- notenam = "yank";
- if (FIXUNDO)
- vundkind = VNONE;
- DEL[0] = 0;
- wdot = NOLINE;
- if (notecnt <= vcnt - vcline && notecnt < value(REPORT))
- notecnt = 0;
- vrepaint(cursor);
- return;
- }
- takeout(DEL);
- }
-
- /*
- * Set pkill variables so a put can
- * know how to put back partial text.
- * This is necessary because undo needs the complete
- * line images to be saved, while a put wants to trim
- * the first and last lines. The compromise
- * is for put to be more clever.
- */
- setpk()
- {
-
- if (wcursor) {
- pkill[0] = cursor;
- pkill[1] = wcursor;
- }
- }
-